package com.zp.qualitypractice.rx.event;

import com.jakewharton.rxrelay2.Relay;
import com.zp.qualitypractice.rx.RxBus;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.processors.FlowableProcessor;
import io.reactivex.processors.PublishProcessor;

/**
 * Created by izp on 2017/9/4.
 */

public class EventBus {
    private volatile static EventBus mRxBus;
    private final FlowableProcessor<Object> mFlowableProcessor;
//    private final Relay<Object> mRelay;
    private final SubscriberMethodFinder mSubscriberMethodFinder;
    private static Map<Class<?>, Map<Class<?>, Disposable>> mDisposableMap = new HashMap<>();
//    这个map的泛型是<订阅者所在类, <事件对象类型, 事件对应的Disposable>>。
//    这种结构也强调了在订阅者所在类中，某种事件对象类型必须唯一，不能有多个方法处理同一种事件对象类型

    private EventBus() {
//        FlowableProcessor(来自RxJava2)对应RxJava中的PublishSubject，是Flowable版的Subject,支持了背压
        mFlowableProcessor = PublishProcessor.create().toSerialized();
        mSubscriberMethodFinder = new SubscriberMethodFinder();
    }

    public static EventBus getDefault() {
        if (mRxBus == null) {
            synchronized (EventBus.class) {
                if (mRxBus == null) {
                    mRxBus = new EventBus();
                }
            }
        }
        return mRxBus;
    }

    /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link ThreadMode}.
     * 找到这个订阅者所在类中所有的Rxbus的反射方法，并将这些方法存储在刚才定义的map中。
     */
    public void register(Object subsciber) {
        Class<?> subsciberClass = subsciber.getClass();
        List<SubscriberMethod> subscriberMethods = mSubscriberMethodFinder.findSubscriberMethods(subsciberClass);
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            addSubscriber(subsciber, subscriberMethod);
        }
    }

    /**
     * translate the subscriberMethod to a subscriber,and put it in a cancleable container .
     * 完成Subscribe注解方法解析后，根据方法信息生成订阅者，并存储返回的Disposable，以便以后取消注册用。
     */
    private void addSubscriber(final Object subsciber, final SubscriberMethod subscriberMethod) {
        Class<?> subsciberClass = subsciber.getClass();
        Class<?> eventType = subscriberMethod.getEventType();
//        SubscriberMethod de getEventType，而不是Methed对象的
        Disposable disposable = mFlowableProcessor.ofType(eventType).observeOn(subscriberMethod.getThreadMode())
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {
                        invokeMethod(subsciber, subscriberMethod, o);
                    }
                });
//        这个subsciberClass注册所在类
        Map<Class<?>, Disposable> disposableMap = mDisposableMap.get(subsciberClass);
        if (disposableMap == null) {
            disposableMap = new HashMap<>();
            mDisposableMap.put(subsciberClass, disposableMap);
        }
        disposableMap.put(eventType, disposable);
    }

    /**
     * call the subscriber method annotationed with receiverd event.
     */
    private void invokeMethod(Object subscriber, SubscriberMethod subscriberMethod, Object obj) {
        try {
//            执行方法
            subscriberMethod.getMethod().invoke(subscriber, obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Posts the given event to the RxBus.
     */
    public void post(Object obj) {
        if (mFlowableProcessor.hasSubscribers()) {
            mFlowableProcessor.onNext(obj);
        }
    }


    /**
     * Unregisters the given subscriber from all event classes.
     */
    public void unregister(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        Map<Class<?>, Disposable> disposableMap = mDisposableMap.get(subscriberClass);
        if (disposableMap == null) {
            throw new IllegalArgumentException(subscriberClass.getSimpleName() + " haven't registered RxBus");
        }
        Set<Class<?>> keySet = disposableMap.keySet();
        for (Class<?> evenType : keySet) {
            Disposable disposable = disposableMap.get(evenType);
            disposable.dispose();
        }
        mDisposableMap.remove(subscriberClass);
    }

    /**
     * Unregisters the given subscriber of eventType from all event classes.
     */
    public void unregister(Object subscriber, Class<?> eventType) {
        Class<?> subscriberClass = subscriber.getClass();
        Map<Class<?>, Disposable> disposableMap = mDisposableMap.get(subscriberClass);
        if (disposableMap == null) {
            throw new IllegalArgumentException(subscriberClass.getSimpleName() + " haven't registered RxBus");
        }
        if (!disposableMap.containsKey(eventType)) {
            throw new IllegalArgumentException("The event with type of " + subscriberClass.getSimpleName() + " is not" +
                    " required in " + subscriberClass.getSimpleName());
        }
        Disposable disposable = disposableMap.get(eventType);
        disposable.dispose();
        mDisposableMap.remove(eventType);
    }
}
